package com.truthbean.jeetb.utils.server;

import com.alibaba.fastjson.JSON;
import com.sun.xml.rpc.processor.model.soap.SOAPUnorderedStructureType;
import com.truthbean.jeetb.utils.StringUtils;
import org.glassfish.embeddable.*;
import org.glassfish.embeddable.archive.ScatteredArchive;

import java.io.*;
import java.net.URI;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Created by TruthBean on 2016-10-13 18:03.
 */
public class GlassfishUtils {
    private static final String HTTP_PORT = "jeetb.web.glassfish.httpPort";
    private static final String HTTPS_PORT = "jeetb.web.glassfish.httpsPort";

    public void run(String domain, String warPath){
        File configFile = new File(domain);
        //domain = "c:\\myapp\\embeddedserver\\domains\\domain1\\config\\domain.xml"
        File war = new File(warPath);
        //warPath = "c:\\samples\\simple.war"
        GlassFishRuntime glassfishRuntime = null;
        GlassFish glassfish = null;
        try {
            glassfishRuntime = GlassFishRuntime.bootstrap();
            GlassFishProperties glassfishProperties = new GlassFishProperties();
            glassfishProperties.setConfigFileURI(String.valueOf(configFile.toURI()));
            glassfishProperties.setConfigFileReadOnly(false);
            glassfish = glassfishRuntime.newGlassFish(glassfishProperties);
            glassfish.start();

            Deployer deployer = glassfish.getDeployer();
            deployer.deploy(war, "--force=true");
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("Press Enter to stop server");
        // wait for Enter
        try {
            new BufferedReader(new java.io.InputStreamReader(System.in)).readLine();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            glassfish.dispose();
            glassfishRuntime.shutdown();
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }

    private File basedir = null;

    public void run() {
        GlassFish glassfish = null;
        GlassFishProperties properties = getPropes();
        System.out.println(JSON.toJSON(properties));
        int httpPort = (int) properties.getPort("http-listener");
        System.out.println(httpPort);
        try {
            /**
             * Start GlassFish in embedded mode to run at 9590 http port.
             */
            glassfish = GlassFishRuntime.bootstrap().newGlassFish(properties);
            glassfish.start();

            addShutdownHook(glassfish); // Shutdown GlassFish when Ctrl+C pressed from terminal.

            /**
             * Programatically create JDBC connection pool and resource with
             * embedded datasource.
             */
            CommandRunner cr = glassfish.getCommandRunner();
            CommandResult result = cr.run("create-jdbc-connection-pool",
                    "--datasourceclassname=org.apache.derby.jdbc.EmbeddedDataSource",
                    "--restype=javax.sql.DataSource",
                    "--property=DatabaseName=embedded-samples:connectionAttributes=;create\\=true",
                    "default_derby_pool");
            System.out.println(result.getOutput());
            result = cr.run("create-jdbc-resource",
                    "--connectionpoolid=default_derby_pool",
                    "jdbc/default_directory");
            System.out.println(result.getOutput());

            String contextRoot = "";

            Deployer deployer = glassfish.getDeployer();

            URI archive = null;
            try {
                archive = createScatteredArchive();
            } catch (IOException ex) {
                Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex);
            }
            /*
             * Deploy the application by specifying the custom contextroot.
             */
            String appName = deployer.deploy(archive, "--contextroot=" + contextRoot);

            System.out.println("Deployed [ " + appName + " ]");
            System.out.println("\nAccess the application at http://localhost:" +
                    httpPort + "/" + contextRoot + "\n");

            waitForQuitCommand();

            if (appName != null) {
                deployer.undeploy(appName);
            }
            cleanup();

        } catch (GlassFishException e) {
            e.printStackTrace();
        } finally {
            if (glassfish != null) {
                try {
                    glassfish.dispose();
                } catch (GlassFishException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private GlassFishProperties getPropes(){
        GlassFishProperties glassfishProperties = new GlassFishProperties();
        Properties properties = new Properties();
        File file = new File(getClass().getClassLoader().getResource("app.properties").getFile());
        try {
            properties.load(new FileInputStream(file));
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("app.properties is not found in resources");
        }
        if(!properties.isEmpty()){
            if(properties.containsKey(HTTP_PORT)){
                String httpPortStr = properties.getProperty(HTTP_PORT);
                if(StringUtils.isNumber(httpPortStr)){
                    int httpPort = Integer.parseInt(httpPortStr);
                    glassfishProperties.setPort("http-listener", httpPort);
                } else {
                    glassfishProperties.setPort("http-listener", 9080);
                }
            }
            if(properties.contains(HTTPS_PORT)){
                int httpsPort = Integer.parseInt(properties.getProperty(HTTPS_PORT));
                if(httpsPort > 0){
                    glassfishProperties.setPort("https-listener", httpsPort);
                } else {
                    glassfishProperties.setPort("https-listener", 9443);
                }
            }
        }

        return glassfishProperties;
    }

    private void cleanup(){
        File embedded = new File(basedir, "embedded");
        cleanup(embedded);
        embedded.delete();

        File derbylog = new File(basedir, "derby.log");
        cleanup(derbylog);
    }

    private void cleanup(File f){

        if (f.isDirectory()) {
            List<File> files = Arrays.asList(f.listFiles());
            boolean delete = false;
            for (File file : files) {
                if (file.isDirectory()) {
                    cleanup(file);
                }
                delete = file.delete();

            }
        } else {
            f.delete();
        }

    }

    private static void waitForQuitCommand() {
        while (true) {
            System.out.println("Type quit when you finish accessing the application");
            String command = null;
            try {
                command = new BufferedReader(
                        new InputStreamReader(System.in)).readLine();
            } catch (Exception ex) {
                ex.printStackTrace();
                break;
            }
            System.out.println(command);
            if (command != null && command.trim().equalsIgnoreCase("quit")) {
                break;
            }
        }
    }

    private void addShutdownHook(final GlassFish gf) {
        Runtime.getRuntime().addShutdownHook(
                new Thread("GlassFish Shutdown Hook") {
                    public void run() {
                        try {
                            if (gf != null) {
                                gf.dispose();
                            }
                        } catch (Exception ex) {
                        }
                    }
                });
    }

    private URI createScatteredArchive() throws IOException  {

        //File currentDirectory = new File(System.getProperty("user.dir"));
        File currentDirectory = new File(getClass().getClassLoader().getResource("app.properties").getPath());

        currentDirectory = currentDirectory.getParentFile().getParentFile();

        if (currentDirectory.getName().equals("dist")){
            //basedir = currentDirectory.getParentFile();
            //TODO
        } else {
            basedir = currentDirectory.getParentFile();
        }

        String directory = basedir.toString().replace(System.getProperty("user.dir") + File.separator, "");

        // Create a scattered web application.
        ScatteredArchive archive = new ScatteredArchive(directory, ScatteredArchive.Type.WAR, new File(basedir, ""));

        try {
            // target/classes directory contains my complied servlets
            archive.addClassPath(new File(new File((basedir),"target"), "classes"));

        } catch (IOException ex) {
            Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex);
        }

        return archive.toURI();
    }
}
